using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Model.Runtime;
using Kaleida.ServiceMonitor.Model.Statistics;
using System.Linq;

namespace Kaleida.ServiceMonitor.Model
{
    public class OperationStatisticsMap
    {
        private readonly ProcessingStatistics combined = new ProcessingStatistics("Combined", "Combined operations");
        private readonly Dictionary<IOperation,ProcessingStatistics> map = new Dictionary<IOperation, ProcessingStatistics>();

        public ProcessingStatistics Combined
        {
            get { return combined; }
        }

        public IList<ProcessingStatistics> Operations
        {
            get { return map.Values.ToList().AsReadOnly(); }
        }

        public IList<ProcessingStatistics> AllValues
        {
            get
            {
                lock (this)
                {
                    var all = new List<ProcessingStatistics> {combined};
                    all.AddRange(map.Values);
                    return all.AsReadOnly();
                }
            }
        }

        public void Initialise(IList<IOperation> operations)
        {
            lock (this)
            {
                combined.Reset();
                map.Clear();
                foreach (var operation in operations)
                {
                    var position = operations.IndexOf(operation) + 1;
                    var name = "Operation " + position.ToString("0");
                    map.Add(operation, new ProcessingStatistics(name, operation.Description));
                }
            }
        }

        private void TryUpdateStatistics([NotNull] IOperation operation, Action<ProcessingStatistics> updateAction)
        {
            if (operation == null) throw new ArgumentNullException("operation");

            lock (this)
            {
                ProcessingStatistics stats;
                if (map.TryGetValue(operation, out stats))
                {
                    updateAction(stats);
                }
            }
        }

        public void ResetAll()
        {
            combined.Reset();

            lock (this)
            {
                foreach (var entry in map)
                {
                    entry.Value.Reset();
                }
            }
        }

        internal void Start()
        {
            lock (this)
            {
                foreach(var stats in AllValues)
                    stats.Start();
            }
        }

        internal void IncrementSuccessCount(IOperation operation, TimeSpan elapsed)
        {
            lock (this)
            {
                combined.IncrementSuccessCount(elapsed);
                TryUpdateStatistics(operation, i => i.IncrementSuccessCount(elapsed));
            }
        }

        internal void IncrementFailureCount(IOperation operation)
        {
            lock (this)
            {
                combined.IncrementFailureCount();
                TryUpdateStatistics(operation, i => i.IncrementFailureCount());
            }
        }
    }
}